package com.zentrale1.noodlemail.tests;

import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

import org.junit.Test;

import com.zentrale1.noodlemail.*;
import com.zentrale1.noodlemail.classifiers.*;

/*
 * The MIT License
 * 
 * Copyright (c) 2010 Andreas Fleig
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */


public class ClassifierTest 
{
	NoodleClassifier nb;
	//String testPath = "/home/afleig/Downloads/maildir/rogers-b";
	String testPath = "/home/afleig/Downloads/maildir/williams-w3";
	//String testPath = "/media/ramdisk/beck-s";
	
	
	HashMap<String, Boolean> visited = new HashMap<String, Boolean>();
	
	public static void main(String[] args)
	{
		// parameters
		String classifier = null;
		String path = null;
		for (int i=0; i<args.length; i++) {
			if (args[i].charAt(0) == '-') {
				if (!(args[i].length() >= 2 && 
						(args[i].charAt(1) != 'X' || args[i].charAt(1) == 'D')))
				{
					i++;
				}
			} else {
				if (classifier == null)
					classifier = args[i];
				else
					path = args[i];
			}
		}
		
		System.out.println("-------------------------------\n"
				+ "Testing "+classifier+" on "+path);
		ClassifierTest c = new ClassifierTest();
		long start = System.currentTimeMillis();
		Maildir md = new Maildir(path);
		String[] f = c.listFolders(md);
		c.nb = ClassifierFactory.getClassifier(classifier, f);
		System.out.println("training inbox");
		c.trainHalf(md, "inbox");
		Maildir[] folders = md.getFolders();
		for (Maildir folder : folders) {
			folder.getPath();
			String[] pathElements = folder.getPath().split("/");
			String name = pathElements[pathElements.length-1];
			System.out.println("training "+name);
			c.trainHalf(folder, name);
		}
		
		int correct = 0;
		int mails = 0;
		correct += c.classifyHalf(md, "inbox");
		mails += md.listMessages(false).length/2;
		for (Maildir folder : folders) {
			folder.getPath();
			String[] pathElements = folder.getPath().split("/");
			String name = pathElements[pathElements.length-1];
			correct += c.classifyHalf(folder, name);
			mails += folder.listMessages(false).length/2;
		}
		double p = new Double(correct) / new Double(mails);
		long dur = System.currentTimeMillis() - start;
		System.out.println("\ntotal precision: "+p);
		System.out.println("===============");
		System.out.println("total mails: "+mails*2);
		System.out.println("categories: "+(folders.length+1));
		System.out.println("runtime: "+dur+" ms");
		System.out.println("-------------------------------\n");
	}
	
	private String[] listFolders(Maildir maildir)
	{
		Maildir[] subFolders = maildir.getFolders();
		ArrayList<String> folderList = new ArrayList<String>();
		folderList.add("inbox");
		for (Maildir m : subFolders) {
			folderList.add(m.getName());
		}
		return folderList.toArray(new String[folderList.size()]);
	}
	
	//@Test
	/**
	 * Trains the classifier on all data, and tests all data. Not a real test 
	 * case.
	 */
	public void train()
	{
		Maildir md = new Maildir(testPath);
		nb = new NoodleBayes(listFolders(md));
		System.out.println("training inbox");
		trainAll(md, "inbox");
		Maildir[] folders = md.getFolders();
		for (Maildir folder : folders) {
			folder.getPath();
			String[] pathElements = folder.getPath().split("/");
			String name = pathElements[pathElements.length-1];
			System.out.println("training "+name);
			trainAll(folder, name);
		}
		
		classifyAll(md, "inbox");
		int correct = 0;
		int mails = 0;
		for (Maildir folder : folders) {
			folder.getPath();
			String[] pathElements = folder.getPath().split("/");
			String name = pathElements[pathElements.length-1];
			correct += classifyAll(folder, name);
			mails += folder.listMessages(false).length;
		}
		double p = new Double(correct) / new Double(mails);
		System.out.println("\ntotal precision: "+p);
		System.out.println("===============");
		
		assertTrue(true);
	}
	
	@Test
	/**
	 * Trains the classifier on half the data, classifies the other half. Not
	 * a real test case.
	 */
	public void halfTest()
	{
		long start = System.currentTimeMillis();
		Maildir md = new Maildir(testPath);
		String[] f = listFolders(md);
		for (String folder : f) {
			System.out.println("folder: "+folder);
		}
		nb = new NoodleBayes(listFolders(md));
		System.out.println("training inbox");
		trainHalf(md, "inbox");
		Maildir[] folders = md.getFolders();
		for (Maildir folder : folders) {
			folder.getPath();
			String[] pathElements = folder.getPath().split("/");
			String name = pathElements[pathElements.length-1];
			System.out.println("training "+name);
			trainHalf(folder, name);
		}
		
		int correct = 0;
		int mails = 0;
		correct += classifyHalf(md, "inbox");
		mails += md.listMessages(false).length/2;
		for (Maildir folder : folders) {
			folder.getPath();
			String[] pathElements = folder.getPath().split("/");
			String name = pathElements[pathElements.length-1];
			correct += classifyHalf(folder, name);
			mails += folder.listMessages(false).length/2;
		}
		double p = new Double(correct) / new Double(mails);
		long dur = System.currentTimeMillis() - start;
		System.out.println("\ntotal precision: "+p);
		System.out.println("===============");
		System.out.println("total mails: "+mails*2);
		System.out.println("categories: "+(folders.length+1));
		System.out.println("runtime: "+dur+" ms");
		
		assertTrue(true);
	}
	
	//@Test
	/**
	 * Simple test, that defines 3 terms each for spam and ham. Not a real test 
	 * case.
	 */
	public void simpleTest()
	{
		Mail[] train = new Mail[6];
		train[0] = new Mail();
		train[0].setSubject("viagra");
		train[1] = new Mail();
		train[1].setSubject("pharmacy");
		train[2] = new Mail();
		train[2].setSubject("gold");
		train[3] = new Mail();
		train[3].setSubject("kartoffel");
		train[4] = new Mail();
		train[4].setSubject("karotte");
		train[5] = new Mail();
		train[5].setSubject("apfel");
	
		String[] classes = {"spam", "ham"};
		NoodleClassifier nb = new WekaWrapper(classes);
		for (int i=0; i<3; i++) {
			nb.train(train[i], "spam");
		}
		for (int i=3; i<6; i++) {
			nb.train(train[i], "ham");
		}
		
		Mail[] test = new Mail[3];
		test[0] = new Mail();
		test[0].setSubject("pharmacy");
		test[1] = new Mail();
		test[1].setSubject("viagra karotte");
		test[2] = new Mail();
		test[2].setSubject("apfel karotte gold");
		
		for (Mail t : test) {
			Result[] r = nb.classify(t);
			printResults(t, r);
		}
		
	}
	
	private int classifyAll(Maildir dir, String category)
	{
		String[] paths = dir.listMessages(false);
		int correct = 0;
		for (String path : paths) {
			Mail m = MailFactory.parse(path);
			Result[] results = nb.classify(m);
			if (results[0].getCategory().equals(category))
				correct++;
			//printResults(m, results);
		}
		double p = new Double(correct) / new Double(paths.length);
		System.out.println(category + "\t " + p);
		return correct;
	}
	
	private int classifyHalf(Maildir dir, String category)
	{
		String[] paths = dir.listMessages(false);
		int start = paths.length/2+1;
		int correct = 0;
		
		Mail[] mails = new Mail[paths.length];
		for (int i=0; i<paths.length; i++) {
			String path = paths[i];
			mails[i] = MailFactory.parse(path);
		}
		Arrays.sort(mails);
		
		for (int i=start; i<mails.length; i++) {
			Result[] results = nb.classify(mails[i]);
			if (results[0].getCategory().equals(category))
				correct++;
		}
		
		double p = new Double(correct) / new Double(paths.length-start);
		System.out.println(category + "\t " + p);
		return correct;
	}
	
	private void printResults(Mail m, Result[] results)
	{
		System.out.println("\nsubject: "+m.getSubject());
		for (Result r : results) {
			System.out.println(r);
		}
	}
	
	private void trainAll(Maildir dir, String category)
	{
		String[] paths = dir.listMessages(false);
		for (String path : paths) {
			Mail m = MailFactory.parse(path);
			//if (!visited.containsKey(m.getMessageId())) {
				nb.train(m, category);
				//visited.put(m.getMessageId(), true);
			//}
		}
	}
	
	private void trainHalf(Maildir dir, String category)
	{
		String[] paths = dir.listMessages(false);
		int max = paths.length/2;
		
		Mail[] mails = new Mail[paths.length];
		for (int i=0; i<paths.length; i++) {
			String path = paths[i];
			mails[i] = MailFactory.parse(path);
		}
		Arrays.sort(mails);

		for (int i=0; i<max; i++) {
				nb.train(mails[i], category);
		}
	}
}
